import random
import time
import struct
from enum import IntEnum


class GTPType(IntEnum):
    NONE = 0
    STRING = 1
    BINARY = 2
    COMPRESSED = 3
    ENCRYPT = 4


class GTPHeader(object):
    format_str = '<IIHBBI'
    LEN = 16

    MAGIC_NUM = 19810313
    VERSION = 1001
    LOG_ID_MIN = 100000001
    LOG_ID_MAX = 999999999

    def __init__(self, magic, log_id, ver, ptype, ext_len, con_len):
        self._magic_num = magic
        self._log_id = log_id
        self._version = ver
        self._type = ptype
        self._ext_len = ext_len
        self._con_len = con_len

    @classmethod
    def __cal_log_id(cls):
        random.seed(time.time())
        rand_no = random.randint(0, cls.LOG_ID_MAX - cls.LOG_ID_MIN)
        return cls.LOG_ID_MIN + rand_no

    @classmethod
    def new(cls, ptype: GTPType, ext_len, con_len):
        return cls(cls.MAGIC_NUM,
                   cls.__cal_log_id(),
                   cls.VERSION,
                   ptype,
                   ext_len,
                   con_len
                   )

    @classmethod
    def decode(cls, data: bytes):
        if cls.LEN != len(data):
            return None

        h = struct.unpack(GTPHeader.format_str, data)
        return cls(*h)

    def encode(self):
        return struct.pack(GTPHeader.format_str,
                           self._magic_num, self._log_id, self._version,
                           self._type, self._ext_len, self._con_len
                           )

    def magic(self):
        return self._magic_num

    def log_id(self):
        return self._log_id

    def version(self):
        return self._version

    def gtp_type(self):
        return self._type

    def ext_len(self):
        return self._ext_len

    def con_len(self):
        return self._con_len


class GTPMessage(object):
    def __init__(self, gheader: GTPHeader, msg: bytes, ext: bytes = None):
        self.__header = gheader
        self.__body = msg
        self.__ext = ext

    @classmethod
    def new(cls, gtype: GTPType, con: bytes, ext: bytes = None):
        return cls(GTPHeader.new(gtype,
                                 0 if ext is None else len(ext),
                                 0 if con is None else len(con)),
                   con, ext)

    def encode(self):
        serialise = self.__header.encode()
        if self.__ext:
            serialise += self.__ext

        if self.__body:
            serialise += self.__body

        return serialise

    def header(self):
        return self.__header

    def body(self):
        return self.__body

    def ext_con(self):
        return self.__ext

